/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Initial Developer of the Original Code is
*   Nick Kreeger
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*   Nick Kreeger <nick.kreeger@park.edu>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */

#include "CHMsgComposeService.h"

#import "NSString+Utils.h"
#import <Foundation/Foundation.h>

#import "CompositionWindowController.h"
#import "CompositionWindowDelegate.h"
#import "CHBrowserView.h"
#import "CHMsgUtils.h"
#import "CHMsgCompUtils.h"

#import "nsIMsgAccountManager.h"
#import "nsIMsgAccount.h"
#import "nsIMsgIdentity.h"
#import "nsIMimeMiscStatus.h"
#import "nsIMsgWindow.h"
#import "nsIMsgHdr.h"

#import "nsIMsgCompFields.h"

#import "nsMsgBaseCID.h"
#import "nsMsgCompCID.h"

#import "nsIDOMWindow.h"
#import "nsIDOMWindowInternal.h"

#import "nsMsgMimeCID.h"
#import "nsNetUtil.h"
#import "nsIMsgMailNewsUrl.h"
#import "nsIMsgMessageService.h"
#import "nsIMimeStreamConverter.h"
#import "nsIStreamConverter.h"
#import "nsISmtpUrl.h"
#import "nsIParser.h"
#import "nsParserCIID.h"
#import "nsIContentSink.h"
#import "mozISanitizingSerializer.h"
#import "nsIPrefService.h"
#import "nsIPrefBranch.h"
#import "nsEscape.h"
#import "nsILocaleService.h"
#import "nsIDateTimeFormat.h"
#import "nsDateTimeFormatCID.h"
#import "nsTextFormatter.h"
#import "nsIMsgHeaderParser.h"
#import "nsMailHeaders.h"
#import "nsMsgComposeStringBundle.h"
#import "nsMsgPrompts.h"
#import "nsIPrompt.h"
#import "nsICharsetConverterManager.h"
#import "nsICiter.h"
#import "CHInternetCiter.h"
#import "nsIAddrBookSession.h"
#import "nsAbBaseCID.h"


/**
 * This class simply relays message events to the compose service
 */
@interface ComposerWindowListener : NSObject
{
  CHMsgComposeService* mComposeService;  // weak ref
}

-(id)initWithComposeService:(CHMsgComposeService*)aComposeService;
-(void)onCompositionWindowClosed:(NSNotification*)aNotification;

@end

@implementation ComposerWindowListener

-(id)initWithComposeService:(CHMsgComposeService*)aComposeService
{
  if ((self = [super init]))
  {
    mComposeService = aComposeService;
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(onCompositionWindowClosed:)
                                                 name:kCompositionWindowWillCloseNotification 
                                               object:nil];
  }
  
  return self;
}

-(void)dealloc
{
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  [super dealloc];
}

-(void)onCompositionWindowClosed:(NSNotification*)aNotification
{
  mComposeService->ComposerWindowDidClose((CompositionWindowController*)[aNotification object]);
}

@end


static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
static NS_DEFINE_CID(kNavDTDCID, NS_CNAVDTD_CID);
static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID);

static void GetReplyHeaderInfo(PRInt32* reply_header_type, 
                               nsString& reply_header_locale,
                               nsXPIDLString& reply_header_authorwrote,
                               nsXPIDLString& reply_header_ondate,
                               nsString& reply_header_separator,
                               nsString& reply_header_colon,
                               nsXPIDLString& reply_header_originalmessage)
{
  nsresult  rv;
  nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
  
  *reply_header_type = 1;
  if(NS_SUCCEEDED(rv)) {
    prefBranch->GetIntPref("mailnews.reply_header_type", reply_header_type);
    
    NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_locale", EmptyString(), reply_header_locale);
    NS_GetLocalizedUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_authorwrote", NS_LITERAL_STRING("%s wrote"), reply_header_authorwrote);
    NS_GetLocalizedUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_ondate", NS_LITERAL_STRING("On %s"), reply_header_ondate);
    NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_separator", NS_LITERAL_STRING(", "), reply_header_separator);
    NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_colon", NS_LITERAL_STRING(":"), reply_header_colon);
    NS_GetLocalizedUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_originalmessage", NS_LITERAL_STRING("--- Original Message ---"), reply_header_originalmessage);
  }
}

static void GetForwardHeaderInfo(nsXPIDLString& inOriginalSubject,
                                 nsXPIDLString& inOriginalDate,
                                 nsXPIDLString& inOriginalAuthor,
                                 nsXPIDLString& inOriginalRecepient,
                                 nsString& reply_header_separator, // not used
                                 nsString& reply_header_colon,     // not used
                                 nsXPIDLString& inFormattedSubject,
                                 nsXPIDLString& reply_header_originalmessage)
{
  nsresult  rv;
  nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
  
  if (NS_SUCCEEDED(rv)) 
  {
    //XXX Check to see if these are prefs already.
    NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.forward_header_subject", NS_LITERAL_STRING("Subject: %s"), inOriginalSubject);
    NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.forward_header_date", NS_LITERAL_STRING("Date: %s"), inOriginalDate);
    NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.forward_header_from", NS_LITERAL_STRING("From: %s"), inOriginalAuthor);
    NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.forward_header_to", NS_LITERAL_STRING("To: %s"), inOriginalRecepient);
    NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_separator", NS_LITERAL_STRING(", "), reply_header_separator);
    NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_colon", NS_LITERAL_STRING(":"), reply_header_colon);
    NS_GetLocalizedUnicharPreferenceWithDefault(prefBranch, "mailnews.forward_subject", NS_LITERAL_STRING("Fwd: %s"), inFormattedSubject);
    NS_GetLocalizedUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_originalmessage", NS_LITERAL_STRING("-------- Original Message --------"), reply_header_originalmessage);
  }
}

NS_IMPL_ISUPPORTS1(CHMsgComposeService, nsIMsgComposeService);

CHMsgComposeService::CHMsgComposeService()
{
  mComposerListener = [[ComposerWindowListener alloc] initWithComposeService:this];
  mComposerWindows = [[NSMutableArray alloc] init];
  mHasStartedABService = PR_FALSE;
}

CHMsgComposeService::~CHMsgComposeService()
{
  [mComposerListener release];
  [mComposerWindows release];
}

nsresult 
CHMsgComposeService::OpenWindow(nsIMsgComposeParams* aMsgComposeParams)
{
  if (!mHasStartedABService)
  {
    // Start the AB service, this will make sure that a gecko AB database is created.
    nsCOMPtr<nsIAddrBookSession> addrBookSession = do_GetService(NS_ADDRBOOKSESSION_CONTRACTID);
    mHasStartedABService = PR_TRUE;
  }
  
  CompositionWindowController* compWinCont = [[CompositionWindowController alloc] init];
  [compWinCont initializeComposer:aMsgComposeParams];
  
  // Perform cleanup, this sucks because gecko events lag behind the call to 
  // |UnregisterComposeWindow| and we can't dealloc objects there. For now, keep 
  // the controllers stashed in the array, and remove windows that have completed
  // their compose process when we open a new window.
  NSEnumerator* compWinContEnum = [mComposerWindows objectEnumerator];
  CompositionWindowController* curCompWinController = nil;
  while ((curCompWinController = [compWinContEnum nextObject]))
    if ([curCompWinController isComposeProcessDone] || ![[curCompWinController window] isVisible]) // |isVisible| breaks.
      [mComposerWindows removeObject:curCompWinController];
  
  // Add here, until the above is fixed
  [mComposerWindows addObject:compWinCont];
  [compWinCont release];
  
  return NS_OK;
}

NS_IMETHODIMP 
CHMsgComposeService::OpenComposeWindow(const char* msgComposeWindowURL,
                                       const char* aOriginalMsgURI,
                                       MSG_ComposeType aType,
                                       MSG_ComposeFormat aFormat,
                                       nsIMsgIdentity* aMsgIdentity,
                                       nsIMsgWindow* aMsgWindow)
{
  nsresult rv;
  PRBool shouldOpenWindow = PR_FALSE;  // not using right now
  nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams;
  
  pMsgComposeParams = do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv);
  if (NS_SUCCEEDED(rv) && pMsgComposeParams)
  {
    pMsgComposeParams->SetType(aType);
    pMsgComposeParams->SetFormat(aFormat);
    pMsgComposeParams->SetIdentity(aMsgIdentity);
    
    nsCOMPtr<nsIMsgCompFields> pMsgCompFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv);
    if (NS_SUCCEEDED(rv) && pMsgCompFields)
    {
      pMsgComposeParams->SetComposeFields(pMsgCompFields);
      shouldOpenWindow = PR_TRUE;
    }
    
    if (aOriginalMsgURI /*&& *aOriginalMsgURI*/)
    {
      pMsgComposeParams->SetOriginalMsgURI(aOriginalMsgURI);
    }
  }
  
  return OpenWindow(pMsgComposeParams);
}

NS_IMETHODIMP 
CHMsgComposeService::OpenComposeWindowWithURI(const char* aMsgComposeWindowURL, nsIURI* aURI)
{
  nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams;
  nsresult rv = GetParamsForMailto(aURI, getter_AddRefs(pMsgComposeParams));
  if (NS_SUCCEEDED(rv))
    rv = OpenComposeWindowWithParams(aMsgComposeWindowURL, pMsgComposeParams);
  return rv;
}

NS_IMETHODIMP 
CHMsgComposeService::OpenComposeWindowWithParams(const char* msgComposeWindowURL, nsIMsgComposeParams* params)
{
  NS_ENSURE_ARG_POINTER(params);
  
  //XXX pass the window URL?
  return OpenWindow(params);
}

void 
CHMsgComposeService::ComposerWindowDidClose(CompositionWindowController* aCompWinController)
{
  //[mComposerWindows removeObject:aCompWinController];
}

NS_IMETHODIMP 
CHMsgComposeService::InitCompose(nsIDOMWindowInternal* aWindow, 
                                 nsIMsgComposeParams* inParams,
                                 nsIMsgCompose** aRetval)
{
  nsresult rv;
  
  nsCOMPtr<nsIMsgCompose> msgCompose = do_CreateInstance(NS_MSGCOMPOSE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  
  rv = msgCompose->Initialize(aWindow, inParams);
  NS_ENSURE_SUCCESS(rv, rv);
  
  NS_IF_ADDREF(*aRetval = msgCompose);
  
  return rv;
}

NS_IMETHODIMP 
CHMsgComposeService::GetDefaultIdentity(nsIMsgIdentity** aRetval)
{
  NS_ENSURE_ARG_POINTER(aRetval);
  *aRetval = nsnull;
  
  nsresult rv;
  nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
  if (NS_SUCCEEDED(rv) && accountManager)
  {
    nsCOMPtr<nsIMsgAccount> defaultAccount;
    rv = accountManager->GetDefaultAccount(getter_AddRefs(defaultAccount));
    if (NS_SUCCEEDED(rv) && defaultAccount)
      defaultAccount->GetDefaultIdentity(aRetval);
  }
  
  return rv;
}

NS_IMETHODIMP 
CHMsgComposeService::TimeStamp(const char *label, PRBool resetTime)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP 
CHMsgComposeService::GetLogComposePerformance(PRBool *aLogComposePerformance)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP 
CHMsgComposeService::DetermineComposeHTML(nsIMsgIdentity *aIdentity, MSG_ComposeFormat aFormat, PRBool *_retval)
{
  *_retval = PR_FALSE;
  return NS_OK;
}

NS_IMETHODIMP 
CHMsgComposeService::CacheWindow(nsIDOMWindowInternal *aWindow, PRBool aComposeHTML, nsIMsgComposeRecyclingListener *listener)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP 
CHMsgComposeService::IsCachedWindow(nsIDOMWindowInternal *aWindow, PRBool *_retval)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP 
CHMsgComposeService::GetParamsForMailto(nsIURI *aURI, nsIMsgComposeParams **aParams)
{
  nsresult rv = NS_OK;
  if (aURI)
  { 
    nsCOMPtr<nsIMailtoUrl> aMailtoUrl;
    rv = aURI->QueryInterface(NS_GET_IID(nsIMailtoUrl), getter_AddRefs(aMailtoUrl));
    printf("rv = %ui", rv);
    if (NS_SUCCEEDED(rv))
    {
      MSG_ComposeFormat requestedComposeFormat = nsIMsgCompFormat::Default;
      nsXPIDLCString aToPart;
      nsXPIDLCString aCcPart;
      nsXPIDLCString aBccPart;
      nsXPIDLCString aSubjectPart;
      nsXPIDLCString aBodyPart;
      nsXPIDLCString aNewsgroup;
      nsXPIDLCString aRefPart;
      nsXPIDLCString aHTMLBodyPart;
      
       // we are explictly not allowing attachments to be specified in mailto: urls
       // as it's a potential security problem.
       // see bug #99055
      aMailtoUrl->GetMessageContents(getter_Copies(aToPart), getter_Copies(aCcPart), 
                                     getter_Copies(aBccPart), nsnull /* from part */,
                                     nsnull /* follow */, nsnull /* organization */, 
                                     nsnull /* reply to part */, getter_Copies(aSubjectPart),
                                     getter_Copies(aBodyPart), getter_Copies(aHTMLBodyPart) /* html part */, 
                                     getter_Copies(aRefPart), nsnull /* attachment part, must always null, see #99055 */,
                                     nsnull /* priority */, getter_Copies(aNewsgroup), nsnull /* host */,
                                     &requestedComposeFormat);
      
      nsAutoString sanitizedBody;
      
      // Since there is a buffer for each of the body types ('body', 'html-body') and
      // only one can be used, we give precedence to 'html-body' in the case where
      // both 'body' and 'html-body' are found in the url.
      NS_ConvertUTF8toUTF16 rawBody(aHTMLBodyPart);
      if(rawBody.IsEmpty())
        CopyUTF8toUTF16(aBodyPart, rawBody);
      
      // For now, we are just sending with plain/text. So the HTML sanitizing will 
      // have to be implemented later.
      
      nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv));
      if (NS_SUCCEEDED(rv) && pMsgComposeParams)
      {
        pMsgComposeParams->SetType(nsIMsgCompType::MailToUrl);
        pMsgComposeParams->SetFormat(nsIMsgCompFormat::PlainText);
        
        nsCOMPtr<nsIMsgCompFields> pMsgCompFields (do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv));
        if (pMsgCompFields)
        {
          //ugghh more conversion work!!!!
          pMsgCompFields->SetTo(NS_ConvertUTF8toUTF16(aToPart));
          pMsgCompFields->SetCc(NS_ConvertUTF8toUTF16(aCcPart));
          pMsgCompFields->SetBcc(NS_ConvertUTF8toUTF16(aBccPart));
          pMsgCompFields->SetNewsgroups(NS_ConvertUTF8toUTF16(aNewsgroup));
          pMsgCompFields->SetReferences(aRefPart);
          pMsgCompFields->SetSubject(NS_ConvertUTF8toUTF16(aSubjectPart));
          pMsgCompFields->SetBody(rawBody);
          pMsgComposeParams->SetComposeFields(pMsgCompFields);
          
          NS_ADDREF(*aParams = pMsgComposeParams);
          return NS_OK;
        }
      } // if we created msg compose params....
    } // if we had a mailto url
  } // if we had a url...
  
  // if we got here we must have encountered an error
  *aParams = nsnull;
  return NS_ERROR_FAILURE;
}

NS_IMETHODIMP 
CHMsgComposeService::ForwardMessage(const nsAString & forwardTo, 
                                    nsIMsgDBHdr *aMsgHdr,
                                    nsIMsgWindow *aMsgWindow, 
                                    nsIMsgIncomingServer *aServer)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP 
CHMsgComposeService::ReplyWithTemplate(nsIMsgDBHdr *msgHdr,
                                       const char *templateUri, 
                                       nsIMsgWindow *aMsgWindow, 
                                       nsIMsgIncomingServer *server)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP 
CHMsgComposeService::RegisterComposeWindow(nsIDOMWindowInternal *aWindow, nsIMsgCompose *aMsgCompose)
{
#if DEBUG
  printf("CHMsgComposeService::RegisterComposeWindow\n");
#endif
  return NS_OK;
}

NS_IMETHODIMP 
CHMsgComposeService::UnregisterComposeWindow(nsIDOMWindowInternal *aWindow)
{
#if DEBUG
  printf("CHMsgComposeService::UnregisterComposeWindow()\n");
#endif
  return NS_OK;
}

NS_IMETHODIMP 
CHMsgComposeService::GetMsgComposeForWindow(nsIDOMWindowInternal *aWindow, nsIMsgCompose **_retval)
{
  return NS_OK;
}

#pragma mark -

NS_IMPL_ISUPPORTS1(CHQuotingOutputStreamListener, nsIStreamListener);

CHQuotingOutputStreamListener::CHQuotingOutputStreamListener(const char* aOriginalMsgUri,
                                                             nsIMsgDBHdr* aOriginalMsgHdr,
                                                             PRBool inQuoteHeaders,
                                                             PRBool inHeadersOnly,
                                                             nsIMsgIdentity* aIdentity,
                                                             PRBool inIsInlineForward,
                                                             const char* aCharset,
                                                             PRBool inCharetOverride,
                                                             PRBool inQuoteOriginal)
: mQuoteHeaders(inQuoteHeaders)
, mUseHeadersOnly(inHeadersOnly)
, mQuoteOriginal(inQuoteOriginal)
, mIdentity(aIdentity)
, mUnicodeBufferCharacterLength(0)
, mUnicodeConversionBuffer(nsnull)
{
  nsresult rv;
  
  if (!mUseHeadersOnly)
  {
    nsXPIDLString replyHeaderOriginalmessage;
    // For the built message body...
    if (aOriginalMsgHdr && !mQuoteHeaders)
    {
      // Setup the cite information...
      nsXPIDLCString messageId;
      if (NS_SUCCEEDED(aOriginalMsgHdr->GetMessageId(getter_Copies(messageId))))
      {
        if (!messageId.IsEmpty())
        {
          nsCAutoString buf;
          mCiteReference.AssignLiteral("mid:");
          AppendASCIItoUTF16(NS_EscapeURL(messageId, esc_FileBaseName | esc_Forced, buf),
                             mCiteReference);
        }
      }
      
      PRInt32 replyOnTop = 0;
      mIdentity->GetReplyOnTop(&replyOnTop);
      if (replyOnTop == 1)
        mCitePrefix.AppendLiteral("\n\n");
      
      PRBool header, headerDate, headerTo, headerFrom, headerSubject;
      PRInt32 replyHeaderType;
      nsAutoString replyHeaderLocale;
      nsXPIDLString replyHeaderAuthorwrote;
      nsXPIDLString replyHeaderOndate;
      nsXPIDLString replyHeaderSubject;
      nsXPIDLString forwardFormattedSubject;
      nsXPIDLString replyHeaderRecepient;
      nsAutoString replyHeaderSeparator;
      nsAutoString replyHeaderColon;
      
      if (inIsInlineForward)
      {
        GetForwardHeaderInfo(replyHeaderSubject,
                             replyHeaderOndate,
                             replyHeaderAuthorwrote,
                             replyHeaderRecepient,
                             replyHeaderSeparator,
                             replyHeaderColon,
                             forwardFormattedSubject,
                             replyHeaderOriginalmessage);
        
        header = PR_TRUE;
        headerDate = PR_TRUE;
        headerTo = PR_TRUE;
        headerSubject = PR_TRUE;
      }
      else
      {
      // Get the header type, local, and strings from pref.
        GetReplyHeaderInfo(&replyHeaderType, 
                           replyHeaderLocale,
                           replyHeaderAuthorwrote,
                           replyHeaderOndate,
                           replyHeaderSeparator,
                           replyHeaderColon,
                           replyHeaderOriginalmessage);
        
        // Never fetch this for replies
        headerTo = PR_FALSE;
        headerSubject = PR_FALSE;
        
        switch(replyHeaderType)
        {
          
          case 0:  // No reply header at all
            header = PR_FALSE;
            headerDate = PR_FALSE;
            break;
            
          case 2:  // Insert both the original author and date in the reply header (date followed by author)
          case 3:  // Insert both the original author and date in the reply header (author followed by date)
            header = PR_TRUE;
            headerDate = PR_TRUE;
            break;
            
          case 4:  // XXX implement user specified header
          case 1:  // Default is to only view the author. We will reconsider this decision when bug 75377 is fixed.
          default:
            header = PR_TRUE;
            headerDate = PR_FALSE;
            break;
        }
      }
      
      nsAutoString citePrefixDate;
      nsAutoString citePrefixAuthor;
      nsAutoString citePrefixSubject;
      nsAutoString citePrefixRecepients;
      
      if (header)
      {
        if (headerDate)
        {
          nsCOMPtr<nsIDateTimeFormat> dateFormatter = do_CreateInstance(kDateTimeFormatCID, &rv);
          if (NS_SUCCEEDED(rv))
          {
            PRTime originalMsgDate;
            rv = aOriginalMsgHdr->GetDate(&originalMsgDate);
            
            if (NS_SUCCEEDED(rv))
            {
              nsAutoString formattedDateString;
              nsCOMPtr<nsILocale> locale;
              nsCOMPtr<nsILocaleService> localeService(do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv));
              
              // Format date using "mailnews.reply_header_locale", if empty then use application default locale.
              if (!replyHeaderLocale.IsEmpty())
                rv = localeService->NewLocale(replyHeaderLocale, getter_AddRefs(locale));
              
              if (NS_SUCCEEDED(rv))
              {
                rv = dateFormatter->FormatPRTime(locale,
                                                 kDateFormatShort,
                                                 kTimeFormatNoSeconds,
                                                 originalMsgDate,
                                                 formattedDateString);
                if (NS_SUCCEEDED(rv)) 
                {
                  // take care "On %s"
                  PRUnichar *formatedString = nsnull;
                  formatedString = nsTextFormatter::smprintf(replyHeaderOndate.get(), 
                                                             NS_ConvertUCS2toUTF8(formattedDateString.get()).get());
                  if (formatedString) 
                  {
                    citePrefixDate.Assign(formatedString);
                    nsTextFormatter::smprintf_free(formatedString);
                  }
                }
              }
            }
          }
        }  // end if !headerdate
        
        nsXPIDLCString author;
        rv = aOriginalMsgHdr->GetAuthor(getter_Copies(author));
        if (NS_SUCCEEDED(rv))
        {
          nsXPIDLCString decodedString;
          mMimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID);
          // Decode header, the result string is null if the input is non MIME encoded ASCII.
          if (mMimeConverter)
            mMimeConverter->DecodeMimeHeader(author.get(), getter_Copies(decodedString), aCharset, inCharetOverride);
          
          nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
          
          if (parser)
          {
            nsXPIDLCString authorName;
            rv = parser->ExtractHeaderAddressName("UTF-8", decodedString ? decodedString.get() : author.get(),
                                                  getter_Copies(authorName));
            // take care "%s wrote"
            PRUnichar *formatedString = nsnull;
            if (NS_SUCCEEDED(rv) && authorName)
              formatedString = nsTextFormatter::smprintf(replyHeaderAuthorwrote.get(), authorName.get());
            else
              formatedString = nsTextFormatter::smprintf(replyHeaderAuthorwrote.get(), author.get());
            if (formatedString) 
            {
              citePrefixAuthor.Assign(formatedString);
              nsTextFormatter::smprintf_free(formatedString);
            }
          }
        }
        
        if (headerTo)
        {
          nsXPIDLCString receipients;
          rv = aOriginalMsgHdr->GetRecipients(getter_Copies(receipients));
          if (NS_SUCCEEDED(rv) && receipients)
          {
          // This is just for now, run the address through the MIME filters.
            PRUnichar* formatedString = nsnull;
            formatedString = nsTextFormatter::smprintf(replyHeaderRecepient.get(), receipients.get());
            if (formatedString)
            {
              citePrefixRecepients.Assign(formatedString);
              nsTextFormatter::smprintf_free(formatedString);
            }
          }
        }
        
        if (headerSubject)
        {
          nsXPIDLCString subject;
          rv = aOriginalMsgHdr->GetSubject(getter_Copies(subject));
          if (NS_SUCCEEDED(rv) && subject)
          {
            PRUnichar* formatedString = nsnull;
            formatedString = nsTextFormatter::smprintf(replyHeaderSubject.get(), subject.get());
            if (formatedString)
            {
              citePrefixSubject.Assign(formatedString);
              nsTextFormatter::smprintf_free(formatedString);
            }
            
            formatedString = nsTextFormatter::smprintf(forwardFormattedSubject.get(), subject.get());
            if (formatedString)
            {
              mFormattedSubject.Assign(formatedString);
              nsTextFormatter::smprintf_free(formatedString);
            }
          }
        }
        
        if (inIsInlineForward)
        {
          // Yikes the new line character is ugly, maybe there is a better way.
          mCitePrefix.AppendLiteral("\n\n");//XXX temp to work around append at top.
          mCitePrefix.Append(replyHeaderOriginalmessage);
          mCitePrefix.AppendLiteral("\n");
          mCitePrefix.Append(citePrefixSubject);
          mCitePrefix.AppendLiteral("\n");
          mCitePrefix.Append(citePrefixDate);
          mCitePrefix.AppendLiteral("\n");
          mCitePrefix.Append(citePrefixAuthor);
          mCitePrefix.AppendLiteral("\n");
          mCitePrefix.Append(citePrefixSubject);
          mCitePrefix.AppendLiteral("\n\n");
        }
        else
        {
          if (replyHeaderType == 2)
          {
            mCitePrefix.Append(citePrefixDate);
            mCitePrefix.Append(replyHeaderSeparator);
            mCitePrefix.Append(citePrefixAuthor);
          }
          else if (replyHeaderType == 3) 
          {
            mCitePrefix.Append(citePrefixAuthor);
            mCitePrefix.Append(replyHeaderSeparator);
            mCitePrefix.Append(citePrefixDate);
          }
          else
            mCitePrefix.Append(citePrefixAuthor);
          
          mCitePrefix.Append(replyHeaderColon);
        }
      }
    }
    
    if (mCitePrefix.IsEmpty())
    {
      if (replyHeaderOriginalmessage.IsEmpty())
      {
        // This is not likely to happen but load the string if it's not done already.
        PRInt32 replyHeaderType;
        nsAutoString replyHeaderLocale;
        nsXPIDLString replyHeaderAuthorwrote;
        nsXPIDLString replyHeaderOndate;
        nsAutoString replyHeaderSeparator;
        nsAutoString replyHeaderColon;
        GetReplyHeaderInfo(&replyHeaderType, 
                           replyHeaderLocale,
                           replyHeaderAuthorwrote,
                           replyHeaderOndate,
                           replyHeaderSeparator,
                           replyHeaderColon,
                           replyHeaderOriginalmessage);
      }
      mCitePrefix.AppendLiteral("\n\n");
      mCitePrefix.Append(replyHeaderOriginalmessage);
      mCitePrefix.AppendLiteral("\n");
    }
  }
}

CHQuotingOutputStreamListener::~CHQuotingOutputStreamListener()
{
  if (mUnicodeConversionBuffer)
    nsMemory::Free(mUnicodeConversionBuffer);
}

NS_IMETHODIMP 
CHQuotingOutputStreamListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
  return NS_OK;
}

NS_IMETHODIMP 
CHQuotingOutputStreamListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatusCode)
{
  nsresult rv = NS_OK;
  nsAutoString aCharset;
  
  nsCOMPtr<nsIMsgCompose> compose = do_QueryReferent(mWeakComposeObj);
  if (compose) 
  {
    MSG_ComposeType type;
    compose->GetType(&type);
    
    // Assign cite information if available...
    if (!mCiteReference.IsEmpty())
      compose->SetCiteReference(mCiteReference);
    
    if (mHeaders && (type == nsIMsgCompType::Reply || type == nsIMsgCompType::ReplyAll || type == nsIMsgCompType::ReplyToSender ||
                     type == nsIMsgCompType::ReplyToGroup || type == nsIMsgCompType::ReplyToSenderAndGroup || 
                     type == nsIMsgCompType::ForwardInline) && mQuoteOriginal)
    {
      nsCOMPtr<nsIMsgCompFields> compFields;
      compose->GetCompFields(getter_AddRefs(compFields));
      if (compFields)
      {
        aCharset.AssignLiteral("UTF-8");
        nsAutoString recipient;
        nsAutoString cc;
        nsAutoString replyTo;
        nsAutoString mailReplyTo;
        nsAutoString mailFollowupTo;
        nsAutoString newgroups;
        nsAutoString followUpTo;
        nsAutoString messageId;
        nsAutoString references;
        nsAutoString subject;
        nsXPIDLCString outCString;
        PRBool needToRemoveDup = PR_FALSE;
        if (!mMimeConverter)
        {
          mMimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID, &rv);
          NS_ENSURE_SUCCESS(rv, rv);
        }
        nsXPIDLCString charset;
        compFields->GetCharacterSet(getter_Copies(charset));
        
        if (type == nsIMsgCompType::ReplyAll)
        {
          mHeaders->ExtractHeader(HEADER_TO, PR_TRUE, getter_Copies(outCString));
          if (outCString)
            mMimeConverter->DecodeMimeHeader(outCString, recipient, charset);
          
          mHeaders->ExtractHeader(HEADER_CC, PR_TRUE, getter_Copies(outCString));
          if (outCString)
            mMimeConverter->DecodeMimeHeader(outCString, cc, charset);
          
          mHeaders->ExtractHeader(HEADER_MAIL_FOLLOWUP_TO, PR_TRUE, getter_Copies(outCString));
          if (outCString)
            mMimeConverter->DecodeMimeHeader(outCString, mailFollowupTo, charset);
          
          if (! mailFollowupTo.IsEmpty())
          { // handle Mail-Followup-To (http://cr.yp.to/proto/replyto.html)
            compFields->SetTo(mailFollowupTo);
          }
          else
          { // default behaviour for messages without Mail-Followup-To
            if (recipient.Length() > 0 && cc.Length() > 0)
              recipient.AppendLiteral(", ");
            recipient += cc;
            compFields->SetCc(recipient);
          }
          
          needToRemoveDup = PR_TRUE;
        }
        
        mHeaders->ExtractHeader(HEADER_SUBJECT, PR_FALSE, getter_Copies(outCString));
        if (outCString)
          mMimeConverter->DecodeMimeHeader(outCString, subject, charset);
        
        mHeaders->ExtractHeader(HEADER_REPLY_TO, PR_FALSE, getter_Copies(outCString));
        if (outCString)
          mMimeConverter->DecodeMimeHeader(outCString, replyTo, charset);
        
        mHeaders->ExtractHeader(HEADER_MAIL_REPLY_TO, PR_TRUE, getter_Copies(outCString));
        if (outCString)
          mMimeConverter->DecodeMimeHeader(outCString, mailReplyTo, charset);
        
        mHeaders->ExtractHeader(HEADER_NEWSGROUPS, PR_FALSE, getter_Copies(outCString));
        if (outCString)
          mMimeConverter->DecodeMimeHeader(outCString, newgroups, charset);
        
        mHeaders->ExtractHeader(HEADER_FOLLOWUP_TO, PR_FALSE, getter_Copies(outCString));
        if (outCString)
          mMimeConverter->DecodeMimeHeader(outCString, followUpTo, charset);
        
        mHeaders->ExtractHeader(HEADER_MESSAGE_ID, PR_FALSE, getter_Copies(outCString));
        if (outCString)
          mMimeConverter->DecodeMimeHeader(outCString, messageId, charset);
        
        mHeaders->ExtractHeader(HEADER_REFERENCES, PR_FALSE, getter_Copies(outCString));
        if (outCString)
          mMimeConverter->DecodeMimeHeader(outCString, references, charset);
        
        if (type == nsIMsgCompType::ForwardInline && !subject.IsEmpty())
        {
          compFields->SetSubject(mFormattedSubject);
        }
        
        if (! ((type == nsIMsgCompType::ReplyAll) && ! mailFollowupTo.IsEmpty()))
        {
          if (! mailReplyTo.IsEmpty())
          { // handle Mail-Reply-To (http://cr.yp.to/proto/replyto.html)
            compFields->SetTo(mailReplyTo);
            needToRemoveDup = PR_TRUE;
          }
          else if (! replyTo.IsEmpty())
          { // default behaviour for messages without Mail-Reply-To
            compFields->SetTo(replyTo);
            needToRemoveDup = PR_TRUE;
          }
        }
        
        /* Remove this?
          if (! newgroups.IsEmpty())
        {
            if ((type != nsIMsgCompType::Reply) && (type != nsIMsgCompType::ReplyToSender))
              compFields->SetNewsgroups(newgroups);
            if (type == nsIMsgCompType::ReplyToGroup)
              compFields->SetTo(EmptyString());
        }
        */
        
        if (! followUpTo.IsEmpty())
        {
          // Handle "followup-to: poster" magic keyword here
          if (followUpTo.EqualsLiteral("poster"))
          {
            nsCOMPtr<nsIDOMWindowInternal> composeWindow;
            nsCOMPtr<nsIPrompt> prompt;
            compose->GetDomWindow(getter_AddRefs(composeWindow));
            if (composeWindow)
              composeWindow->GetPrompter(getter_AddRefs(prompt));
            
            //XXX kreeger -> Due to some dylib issues (12/9/2006) this has been commented out
            //nsMsgDisplayMessageByID(prompt, NS_MSG_FOLLOWUPTO_ALERT);
            
            // If reply-to is empty, use the from header to fetch
            // the original sender's email
            if (!replyTo.IsEmpty())
              compFields->SetTo(replyTo);
            else
            {
              mHeaders->ExtractHeader(HEADER_FROM, PR_FALSE, getter_Copies(outCString));
              if (outCString)
              {
                nsAutoString from;
                mMimeConverter->DecodeMimeHeader(outCString, from, charset);
                compFields->SetTo(from);
              }
            }
            
            // Clear the newsgroup: header field, because followup-to: poster
            // only follows up to the original sender
            if (! newgroups.IsEmpty())
              compFields->SetNewsgroups(EmptyString());
          }
          else // Process "followup-to: newsgroup-content" here
          {
            if (type != nsIMsgCompType::ReplyToSender)
              compFields->SetNewsgroups(followUpTo);
            if (type == nsIMsgCompType::Reply)
              compFields->SetTo(EmptyString());
          }
        }
        
        if (! references.IsEmpty())
          references.Append(PRUnichar(' '));
        references += messageId;
        compFields->SetReferences(NS_LossyConvertUCS2toASCII(references).get());
        
        /*
         This is broken because of hooks to lower nsIMsgCompFields classes.
         if (needToRemoveDup)
         {
          //Remove duplicate addresses between TO && CC
           char * resultStr;
           nsMsgCompFields* _compFields = (nsMsgCompFields*)compFields.get();  // XXX what is this?
           if (NS_SUCCEEDED(rv))
           {
             nsCString addressToBeRemoved(_compFields->GetTo());
             if (mIdentity)
             {
               nsXPIDLCString email;
               mIdentity->GetEmail(getter_Copies(email));
               addressToBeRemoved += ", ";
               addressToBeRemoved += email;
             }
             
             rv= RemoveDuplicateAddresses(_compFields->GetCc(), addressToBeRemoved.get(), PR_TRUE, &resultStr);
             if (NS_SUCCEEDED(rv))
             {
               _compFields->SetCc(resultStr);
               PR_Free(resultStr);
             }
           }
         }
         */
      }
    }
    
#ifdef MSGCOMP_TRACE_PERFORMANCE
    nsCOMPtr<nsIMsgComposeService> composeService (do_GetService(NS_MSGCOMPOSESERVICE_CONTRACTID));
    composeService->TimeStamp("Done with MIME. Now we're updating the UI elements", PR_FALSE);
#endif
    
    if (mQuoteOriginal)
      compose->NotifyStateListeners(eComposeFieldsReady, NS_OK);
    
#ifdef MSGCOMP_TRACE_PERFORMANCE
    composeService->TimeStamp("Addressing widget, window title and focus are now set, time to insert the body", PR_FALSE);
#endif
    
    if (!mUseHeadersOnly)
      mMsgBody.AppendLiteral("</html>");
    
    // Now we have an HTML representation of the quoted message.
    // If we are in plain text mode, we need to convert this to plain
    // text before we try to insert it into the editor. If we don't, we
    // just get lots of HTML text in the message...not good.
    //
    // XXX not m_composeHTML? /BenB
    PRBool composeHTML = PR_TRUE;
    compose->GetComposeHTML(&composeHTML);
    if (!composeHTML)
    {
      // Downsampling. The charset should only consist of ascii.
      char *target_charset = ToNewCString(aCharset);
      PRBool formatflowed = UseFormatFlowed(target_charset);
      ConvertToPlainText(formatflowed);
      Recycle(target_charset);
    }
    
    compose->ProcessSignature(mIdentity, PR_TRUE, &mSignature);
    
    /*
     This is pretty ugly right now:
     - Rather than setting the editors string value here, we 
     notify |CHMsgComposeWindow| that the body fields are 
     ready. In return, that class requests the body text
     via |GetBodyText()|. In the future it would be nice
     to set all this up for the editor widget here.
     
     
     if (mQuoteOriginal)
     compose->ConvertAndLoadComposeWindow(mCitePrefix,
                                          mMsgBody, mSignature,
                                          PR_TRUE, composeHTML);
     else
     */
      //InsertToCompose(composeHTML);
    
    /*
     XXX this is where we will hook up our own editor.
     
     nsCOMPtr<nsIEditor> editor;
     if (NS_SUCCEEDED(compose->GetEditor(getter_AddRefs(editor))) && editor)
     {
       if (mQuoteOriginal)
         compose->ConvertAndLoadComposeWindow(mCitePrefix,
                                              mMsgBody, mSignature,
                                              PR_TRUE, composeHTML);
       else
         InsertToCompose(editor, composeHTML);
     }
     */
    
    if (mQuoteOriginal)
      compose->NotifyStateListeners(eComposeBodyReady, NS_OK);
    
  }
  return rv;
}

NS_IMETHODIMP
CHQuotingOutputStreamListener::OnDataAvailable(nsIRequest* aRequest, 
                                               nsISupports* aContext, 
                                               nsIInputStream* aInputStream, 
                                               PRUint32 sourceOffset, 
                                               PRUint32 count)
{
  nsresult rv = NS_OK;
  NS_ENSURE_ARG(aInputStream);
  
  if (mUseHeadersOnly)
    return rv;
  
  char *newBuf = (char *)PR_Malloc(count + 1);
  if (!newBuf)
    return NS_ERROR_FAILURE;
  
  PRUint32 numWritten = 0; 
  rv = aInputStream->Read(newBuf, count, &numWritten);
  if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    rv = NS_OK;
  newBuf[numWritten] = '\0';
  if (NS_SUCCEEDED(rv) && numWritten > 0)
  {
    // Create unicode decoder.
    if (!mUnicodeDecoder)
    {
      nsCOMPtr<nsICharsetConverterManager> ccm = 
      do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); 
      if (NS_SUCCEEDED(rv))
      {
        rv = ccm->GetUnicodeDecoderRaw("UTF-8",
                                       getter_AddRefs(mUnicodeDecoder));
      }
    }
    
    if (NS_SUCCEEDED(rv))
    {
      PRInt32 unicharLength;
      PRInt32 inputLength = (PRInt32) numWritten;
      rv = mUnicodeDecoder->GetMaxLength(newBuf, numWritten, &unicharLength);
      if (NS_SUCCEEDED(rv))
      {
        // Use this local buffer if possible.
        const PRInt32 kLocalBufSize = 4096;
        PRUnichar localBuf[kLocalBufSize];
        PRUnichar *unichars = localBuf;
        
        if (unicharLength > kLocalBufSize)
        {
          // Otherwise, use the buffer of the class.
          if (!mUnicodeConversionBuffer ||
              unicharLength > mUnicodeBufferCharacterLength)
          {
            if (mUnicodeConversionBuffer)
              nsMemory::Free(mUnicodeConversionBuffer);
            mUnicodeConversionBuffer = (PRUnichar *) nsMemory::Alloc(unicharLength * sizeof(PRUnichar));
            if (!mUnicodeConversionBuffer)
            {
              mUnicodeBufferCharacterLength = 0;
              PR_Free(newBuf);
              return NS_ERROR_OUT_OF_MEMORY;
            }
            mUnicodeBufferCharacterLength = unicharLength;
          }
          unichars = mUnicodeConversionBuffer;
        }
        
        PRInt32 consumedInputLength = 0;
        PRInt32 originalInputLength = inputLength;
        char *inputBuffer = newBuf;
        PRInt32 convertedOutputLength = 0;
        PRInt32 outputBufferLength = unicharLength; 
        PRUnichar *originalOutputBuffer = unichars;
        do 
        {
          rv = mUnicodeDecoder->Convert(inputBuffer, &inputLength, unichars, &unicharLength);
          if (NS_SUCCEEDED(rv)) 
          {
            convertedOutputLength += unicharLength;
            break;
          }
          
          // if we failed, we consume one byte, replace it with a question mark
          // and try the conversion again.
          unichars += unicharLength;
          *unichars = (PRUnichar)'?';
          unichars++;
          unicharLength++;
          
          mUnicodeDecoder->Reset();
          
          inputBuffer += ++inputLength;
          consumedInputLength += inputLength;
          inputLength = originalInputLength - consumedInputLength;  // update input length to convert
          convertedOutputLength += unicharLength;
          unicharLength = outputBufferLength - unicharLength;       // update output length
          
        } while (NS_FAILED(rv) &&
                 (originalInputLength > consumedInputLength) && 
                 (outputBufferLength > convertedOutputLength));
        
        if (convertedOutputLength > 0)
          mMsgBody.Append(originalOutputBuffer, convertedOutputLength);
      }
    }
  }
  
  PR_FREEIF(newBuf);
  return rv;
}

NS_IMETHODIMP
CHQuotingOutputStreamListener::SetComposeObject(nsIMsgCompose* aMsgComposeObject)
{
  mWeakComposeObj = do_GetWeakReference(aMsgComposeObject);
  return NS_OK;
}

NS_IMETHODIMP 
CHQuotingOutputStreamListener::ConvertToPlainText(PRBool inFormatFlowed /* = PR_FALSE */)
{
  nsresult rv = ConvertBufToPlainText(mMsgBody, inFormatFlowed);
  if (NS_SUCCEEDED(rv))
  {
    /*
     XXX This works! Yay, just need to add the carrets....
    NSString* temp = [NSString stringWith_nsAString:mMsgBody];
    NSLog(@"temp buf = %@", temp);
     */
  }
  if (NS_FAILED(rv))
    return rv;
  return ConvertBufToPlainText(mSignature, inFormatFlowed);
}

NS_IMETHODIMP
CHQuotingOutputStreamListener::SetMimeHeaders(nsIMimeHeaders* aMimeHeaders)
{
  mHeaders = aMimeHeaders;
  return NS_OK;
}

// Utility routine to make a new citer.  This addrefs, of course.
static nsICiter* MakeACiter()
{
  // Make a citer of an appropriate type
  CHInternetCiter* citer = 0;
  nsresult rv;
  nsCOMPtr<nsIPrefBranch> prefBranch =
    do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  if (NS_FAILED(rv)) return 0;
  
  char *citationType = 0;
  rv = prefBranch->GetCharPref("mail.compose.citationType", &citationType);
  
  /* Ignore for now, nsAOLCiter is hidden from our build path ...
  if (NS_SUCCEEDED(rv) && citationType[0] && !strncmp(citationType, "aol", 3))
    citer = new nsAOLCiter;
  else
    */
    citer = new CHInternetCiter;
  
  if (citationType)
    PL_strfree(citationType);
  
  if (citer)
    NS_ADDREF(citer);
  return citer;
}

NS_IMETHODIMP
CHQuotingOutputStreamListener::GetBodyText(NSString** aRetVal)
{
  nsresult rv;
  
  if (!mMsgBody.IsEmpty())
  {
    NSString* bodyText = @"";
    if (!mCitePrefix.IsEmpty())
    {
#if DEBUG
      NSLog(@"mCitePrefix == \n%@", [NSString stringWith_nsAString:mCitePrefix]);
#endif
      //XXX need to know if this is an HTML or plain/text (always plain/text right now)
      mCitePrefix.AppendLiteral("\n");
      bodyText = [bodyText stringByAppendingString:[NSString stringWith_nsAString:mCitePrefix]];
    }
    
    nsCOMPtr<nsIMsgCompose> compose = do_QueryReferent(mWeakComposeObj);
    if (compose) 
    {
      MSG_ComposeType type;
      compose->GetType(&type);
      
      if (type == nsIMsgCompType::Reply ||
          type == nsIMsgCompType::ReplyAll ||
          type == nsIMsgCompType::ReplyToSender)
      {
        nsCOMPtr<nsICiter> citer = dont_AddRef(MakeACiter());
        
        // Let the citer quote it for us:
        nsString quotedStuff;
        rv = citer->GetCiteString(mMsgBody, quotedStuff);
        NS_ENSURE_SUCCESS(rv, rv);
        
        if (!mMsgBody.IsEmpty() && (mMsgBody.Last() != PRUnichar('\n')))
          quotedStuff.Append(PRUnichar('\n'));
        
        // Finally, shove this in our text field
        bodyText = [bodyText stringByAppendingString:[NSString stringWith_nsAString:quotedStuff]];
      }
      
      if (type == nsIMsgCompType::ForwardInline)
      {
        bodyText = [bodyText stringByAppendingString:[NSString stringWith_nsAString:mMsgBody]];
        //XXX insert without quoting?
      }
    }
    
    *aRetVal = bodyText;
    //XXXX
  }
  
  return rv;
}

